<?php

/**
 * Provides an abstraction to mysqli 
 */
class Database {

    private static $host = "";
    private static $user = "";
    private static $password = "";
    private static $database = "";
    private static $instance = null;

    /**
     *
     * @var mysqli 
     */
    private $mysqli = null;
    private $query = "";

    /**
     *
     * @var MySQLi_Result 
     */
    private $resultSet = null;
    private $numRows = 0;
    private $lastId = 0;

    private function __construct() {
        
    }

    /**
     * Connect to a MySQL database.
     * 
     * @param type $host
     * @param type $user
     * @param type $password
     * @param type $database
     * @param type $port
     * @throws DatabaseException 
     */
    public function connect($host, $user, $password, $database, $port) {
        $this->invalidate();
        $this->mysqli = new mysqli($host, $user, $password, $database, $port);

        if ($this->mysqli->connect_errno) {
            throw new DatabaseException("Database connect error: " . $this->mysqli->connect_error);
        }
    }

    /**
     * return an instance of the Database object
     * 
     * @return Database
     */
    public static function getInstance() {

        if (Database::$instance == null) {
            Database::$instance = new Database();
        }

        return Database::$instance;
    }

    private function invalidate(){
        $this->mysqli = null;
        $this->query = '';
        $this->resultSet = null;
        $this->numRows = null;
        $this->lastId = null;
        
    }
    
    private function invalidateResultSet() {
        $this->query = "";
        $this->resultSet = null;
        $this->numRows = 0;
    }

    public function getNumRows() {
        return $this->numRows;
    }

    public function fetchArray() {
        return $this->resultSet->fetch_assoc();
    }

    public function getResultSet() {
        return $this->resultSet->fetch_all(MYSQLI_ASSOC);
    }

    public function query($sql) {
        $this->query = $sql;
        $this->invalidateResultSet();

        $this->resultSet = @$this->mysqli->query($sql);

        if ($this->mysqli->errno) {
            throw new DatabaseException("Database Query Error: " . $this->mysqli->error);
        }

        $this->numRows = 0;
        if ($this->resultSet) {
            $this->numRows = $this->resultSet->num_rows;
        }

        $this->lastId = $this->mysqli->insert_id;
    }

    public function sanitize($value) {
        //return addslashes($value);
        return $this->mysqli->escape_string($value);
    }

    public function errors() {
        if ($this->mysqli->errno) {
            return $this->mysqli->error;
        }
        return false;
    }

    public function prepare($query, $params) {
        $this->mysqli->prepare($query);

        if ($this->mysqli->errno) {
            throw new DatabaseException($this->mysqli->error, $this->mysqli->errno);
        }
    }

    public function insert($table, $values) {


        $colNames = array();
        $colValues = array();

        $types = '';

        foreach ($values as $key => $value) {
            $colNames[] = $key;
            //$colValues[] = "'" . $this->sanitize($value) . "'";
            $colValues[] = '?';

            if (is_numeric($value) && $value % 1 == 0) {
                $types .= 'i';
            } elseif (is_float($value)) {
                $types .= 'd';
            } else {
                $types .= 's';
            }
        }

        $nameString = implode(" , ", $colNames);
        $valueString = implode(" , ", $colValues);


        $sql = "INSERT INTO $table ($nameString) VALUES ($valueString);";

        $this->query($sql);
    }

    public function update($table, $values, $where = false) {
        $colNames = array();
        $colValues = array();
        $whereString = "";

        foreach ($values as $key => $value) {
            $colNames[] = $key;
            $colValues[] = "'" . $this->sanitize($value) . "'";
        }
        unset($key);
        unset($value);


        if (is_array($where)) {
            $counter = 0;
            $whereString = " WHERE ";
            foreach ($where as $key => $value) {
                $whereString .= "$key = '" . $this->sanitize($value) . "'";
                $counter++;
                if ($counter < count($where)) {
                    $whereString .= " AND ";
                }
            }
        }

        $nameString = implode(" , ", $colNames);
        $valueString = implode(" , ", $colValues);


        $sql = "UPDATE $table ($nameString) VALUES ($valueString) WHERE $whereString;";

        $this->query($sql);
    }

    public function getLastInsertId() {
        return $this->mysqli->insert_id;
    }

}

class DatabaseException extends Exception {
    
}

